package com.ant.backstage.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.ant.backstage.facade.ApiReturn;
import com.ant.backstage.facade.MailTemplet.ImageResource;
import com.ant.backstage.facade.code.ApiReturnCode;
import com.ant.backstage.facade.dto.MailTempLetDTO;
import com.ant.backstage.facade.enums.MailTypeEnum;
import com.ant.backstage.facade.exception.BackstageBusinessException;
import com.ant.backstage.facade.request.MailRequest;
import com.ant.backstage.service.SendMailService;
import ma.glasnost.orika.MapperFacade;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service("sendMailService")
public class SendMailServiceImpl implements SendMailService {

    private Logger logger = LoggerFactory.getLogger(SendMailServiceImpl.class);

    //本身邮件的发送者，来自邮件配置
    @Value("${spring.mail.username}")
    private String userName;
    //模板引擎解析对象，用于解析模板
    @Autowired
    private TemplateEngine templateEngine;
    @Autowired
    private MapperFacade mapperFacade;
    //邮件发送的对象，用于邮件发送
    @Autowired
    private JavaMailSender mailSender;

    //默认编码
    public static final String DEFAULT_ENCODING = "UTF-8";

    /**
     * @return 优化点：添加发送邮件记录
     * @Author zhangyu
     * @Description
     * @Date 16:47 2021/12/30
     * @Param 邮件发送
     **/
    @Override
    public ApiReturn sendMail(MailRequest mailRequest) {
        logger.info("邮件发送 入参 mailRequest={}", JSONObject.toJSONString(mailRequest));

        ApiReturn apiReturn = new ApiReturn();
        MailTempLetDTO mailTempLetDTO = mapperFacade.map(mailRequest, MailTempLetDTO.class);
        MailTypeEnum byCode = MailTypeEnum.getByCode(mailTempLetDTO.getMailType());
        if(byCode == null){
            logger.error("邮件类型不存在 mailTempLetDTO={}",mailTempLetDTO);
            throw new BackstageBusinessException(ApiReturnCode.SEND_MAIL_TYPE.getMessage(),ApiReturnCode.SEND_MAIL_TYPE.getCode());
        }
        switch (byCode){
            case NORMAL_MAIL:
                apiReturn = simpleMail(mailTempLetDTO, apiReturn);
                break;
            case TEXT_SLAVE_MAIL:
                apiReturn = sendSimpleTextMail(mailTempLetDTO, apiReturn);
                break;
            case IMG_SLAVE_MAIL:
                apiReturn = sendHtmlTemplateMailActual(mailTempLetDTO, apiReturn);
                break;
            case IMG_MAIL:
                //暂不使用
//                apiReturn = sendHtmlTemplateMailActual(mailTempLetDTO, apiReturn);
                break;
        }
        return apiReturn;
    }
    //简单邮件
    private ApiReturn  simpleMail(MailTempLetDTO mailTempLetDTO, ApiReturn apiReturn){
        //创建一个简单邮件信息对象
        SimpleMailMessage simpleMailMessage = new SimpleMailMessage();
        //设置邮件的基本信息
        this.handleBasicInfo(simpleMailMessage, mailTempLetDTO);
        //发送邮件
        mailSender.send(simpleMailMessage);
        logger.info("发送邮件成功 mailTempLetDTO={}", JSONObject.toJSONString(mailTempLetDTO));
        apiReturn.setApiReturnCode(ApiReturnCode.SUCCESSFUL);
        return apiReturn;
    }

    /**
     * 带附件的邮件
     */
    public ApiReturn sendSimpleTextMail(MailTempLetDTO mailTempLetDTO, ApiReturn apiReturn) {
        //检验参数：邮件主题、收件人、邮件内容必须不为空才能够保证基本的逻辑执行
        if (mailTempLetDTO.getAcceptList().length <= 0 || StringUtils.isBlank(mailTempLetDTO.getContent())) {
            logger.error("邮件发送失败参数：邮件主题、收件人、邮件内容 mailTempLetDTO={}", JSONObject.toJSONString(mailTempLetDTO));
            apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
            apiReturn.setMsg(ApiReturnCode.PARAMS_ERROR.getMessage());
            return apiReturn;
        }
        //附件处理，需要处理附件时，需要使用二进制信息，使用 MimeMessage 类来进行处理
        if (mailTempLetDTO.getAttachmenturl() != null && mailTempLetDTO.getAttachmenturl().length > 0) {
            try {
                //附件处理需要进行二进制传输
                MimeMessage mimeMessage = mailSender.createMimeMessage();
                MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true, DEFAULT_ENCODING);
                //设置邮件的基本信息：这些函数都会在后面列出来
                boolean b = handleBasicInfo(helper, mailTempLetDTO,false);
                if (!b) {
                    logger.error("发送邮件信息异常 附件 mailTempLetDTO={}", JSONObject.toJSONString(mailTempLetDTO));
                    apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
                    apiReturn.setMsg(ApiReturnCode.PARAMS_ERROR.getMessage());
                    return apiReturn;
                }
                //处理附件
                handleAttachment(helper, mailTempLetDTO);
                //发送该邮件
                mailSender.send(mimeMessage);
                apiReturn.setApiReturnCode(ApiReturnCode.SUCCESSFUL);
            } catch (Exception e) {
                logger.error("发送邮件信息异常 附件 mailTempLetDTO={}",
                        JSONObject.toJSONString(mailTempLetDTO));
                apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
                apiReturn.setMsg(ApiReturnCode.PARAMS_ERROR.getMessage());
                return apiReturn;
            }
        }
        return apiReturn;
    }
    /**
     * 可以用来发送带有图片的HTML模板邮件
     * @param /imageResourceSet：图片资源的资源对象
     */
    private ApiReturn sendHtmlTemplateMailActual(MailTempLetDTO mailTempLetDTO, ApiReturn apiReturn){
        try{
            //邮件内容不能为空，需要进行检查
            if(StringUtils.isBlank(mailTempLetDTO.getContent())
                    && !mailTempLetDTO.getMailType().equals(MailTypeEnum.GET_VERIFYCODE_MAIL.getCode())
                    && !mailTempLetDTO.getMailType().equals(MailTypeEnum.IMG_SLAVE_MAIL.getCode())
            ){
                apiReturn.setCode(ApiReturnCode.SEND_MAIL_HTML_CONTENT.getCode());
                apiReturn.setMsg(ApiReturnCode.SEND_MAIL_HTML_CONTENT.getMessage());
                logger.error("HTML模板发送邮件，内容为空 mailTempLetDTO={}",JSONObject.toJSONString(mailTempLetDTO));
                return apiReturn;
            }

            if(mailTempLetDTO.getImgUrl() == null || mailTempLetDTO.getImgUrl().length <= 0 ){
                apiReturn.setCode(ApiReturnCode.SEND_MAIL_HTML_IMGURL.getCode());
                apiReturn.setMsg(ApiReturnCode.SEND_MAIL_HTML_IMGURL.getMessage());
                logger.error("HTML模板发送邮件,没有图片 mailTempLetDTO={}",JSONObject.toJSONString(mailTempLetDTO));
                return apiReturn;
            }

            //处理邮件内容 转化为 Context context 生成验证码 以及图片张数
            Context context = new Context();
            //生成6位验证码
            String verifyCode = String.valueOf(new Random().nextInt(899999) + 100000);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            context.setVariable("verifyCode",verifyCode);
            context.setVariable("dateTime",sdf.format(new Date()));

            List<String> imgUrlList = Arrays.asList(mailTempLetDTO.getImgUrl());
            Set<ImageResource> imageResources = new HashSet<>();
            imgUrlList.stream().forEach(url->{
                //获得图片名不带后缀
                File tempFile =new File(url.trim());
                String fileName =  ImageResource.PLACEHOLDERPREFIX + tempFile.getName()+ String.valueOf(System.nanoTime()); //文件位置，唯一性
                ImageResource imageResource = new ImageResource(fileName,url);
                imageResources.add(imageResource);
            });
            List<String> imagePlaceholder = imageResources.stream().map(ImageResource::getPlaceholder).collect(Collectors.toList());
            context.setVariable("imagePlaceholder",imagePlaceholder);//thymeleaf生成对应的图片标签

            //模板引擎处理模板获取到HTML字符串，这里会可能会抛出一个继承于RuntimeException的模板引擎异常
            String content = templateEngine.process(mailTempLetDTO.getTemplateName(),context);
            MimeMessage mimeMessage = mailSender.createMimeMessage();
            //默认编码为UTF-8 是否发送的邮件是富文本（附件，图片，html等）
            MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true,DEFAULT_ENCODING);
            mailTempLetDTO.setContent(content);
            //处理内联的图片资源的占位转换
            apiReturn = handleInLineImageResourceContent(helper, mailTempLetDTO, imageResources, apiReturn);
            mailTempLetDTO = (MailTempLetDTO) apiReturn.getData();
            logger.info("解析邮件结果 mailTempLetDTO={}",mailTempLetDTO);
            //处理基本信息
            boolean continueProcess = handleBasicInfo(helper,mailTempLetDTO,true);
            if (!continueProcess) {
                logger.error("发送邮件信息异常 图片模板 mailTempLetDTO={}", JSONObject.toJSONString(mailTempLetDTO));
                apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
                apiReturn.setMsg(ApiReturnCode.PARAMS_ERROR.getMessage());
                return apiReturn;
            }
            //内联资源的资源附加，这个必须要放置在设置基本信息的操作后面，或者是全部内容解析完毕后才可以，不能边解析，边占位
            handleInLineImageResource(helper,mailTempLetDTO.getMainTheme(),imageResources);
            //处理附件
            if (mailTempLetDTO.getAttachmenturl() != null && mailTempLetDTO.getAttachmenturl().length > 0) {
                handleAttachment(helper,mailTempLetDTO);
            }
            //发送该邮件
            mailSender.send(mimeMessage);
            // 将验证码添加到 redis中 缓存，有效5分钟 由于需要IP，放到controller
            apiReturn.setData(verifyCode);
            apiReturn.setApiReturnCode(ApiReturnCode.SUCCESSFUL);
            return apiReturn;
        }catch(MessagingException e){
            logger.error("发送邮件信息异常 图片模板 mailTempLetDTO={}", JSONObject.toJSONString(mailTempLetDTO));
            apiReturn.setCode(ApiReturnCode.PARAMS_ERROR.getCode());
            apiReturn.setMsg(ApiReturnCode.PARAMS_ERROR.getMessage());
            return apiReturn;
        }
    }
    /**
     * 处理内嵌图片的模板HTML邮件，返回一个已经修改过后的HTML字符串
     * @param /imageResourceSet：图片资源集合，用于字符集站位填充
     * @param /content
     * @param apiReturn
     * @return ：返回处理后的邮件内容
     */
    private ApiReturn handleInLineImageResourceContent(MimeMessageHelper helper, MailTempLetDTO mailTempLetDTO, Set<ImageResource> imageResources, ApiReturn apiReturn){

        //处理内嵌的HTML图片文件
        if(imageResources != null&& imageResources.size() > 0){
            String content = mailTempLetDTO.getContent();
            //资源的占位符ID
            String rscId;
            //资源的路径
            String resourcePath = null;
            //图片的位置信息
            String placeHolder;
            //图片资源文件
            FileSystemResource resource;
            for(ImageResource imageResource : imageResources){
                //获取图片资源的基本信息
                rscId = imageResource.getId();
                placeHolder = imageResource.getPlaceholder();
                resourcePath = imageResource.getImageFilePath();
                resource = new FileSystemResource(new File(resourcePath));
                //判断图片资源是否存在
                if(!resource.exists()){
                    logger.error("理内嵌图片的模板HTML邮件 异常 图片模板 图片不存在 mailTempLetDTO={},imageResources={},resourcePath={}",
                            JSONObject.toJSONString(mailTempLetDTO), JSONObject.toJSONString(imageResources),resourcePath);
                    apiReturn.setCode(ApiReturnCode.RESOURCE_IS_NOT.getCode());
                    apiReturn.setMsg(ApiReturnCode.RESOURCE_IS_NOT.getMessage());
                    return apiReturn;
                }
                //替换图片资源在HTML中的位置
                content = content.replace("\"" + placeHolder + "\"","\'cid:" + rscId + "\'");
                mailTempLetDTO.setContent(content);
            }
        }
        apiReturn.setApiReturnCode(ApiReturnCode.SUCCESSFUL);
        apiReturn.setData(mailTempLetDTO);
        return apiReturn;
    }
    /**
     * 填充文本数据，因为数据填充必须在设置基本数据后面进行，所以讲内容和数据的填充进行分离
     *
     * @param mimeMessageHelper
     * @param subject：邮件主题，用于日志记录
     * @param imageResourceSet：资源
     */
    private void handleInLineImageResource(MimeMessageHelper mimeMessageHelper,String subject,Set<ImageResource> imageResourceSet){

        if(imageResourceSet != null && imageResourceSet.size() > 0){
            FileSystemResource resource;
            for(ImageResource imageResource : imageResourceSet){
                resource = new FileSystemResource(new File(imageResource.getImageFilePath()));
                if(!resource.exists()){
                    logger.error("添加内联资源 异常 图片模板 图片不存在 subject={},imageResourceSet={},resourcePath={}",
                            JSONObject.toJSONString(subject), JSONObject.toJSONString(imageResourceSet),imageResource.getImageFilePath());
                    continue;
                }
                try{
                    //添加内联资源
                    mimeMessageHelper.addInline(imageResource.getId(),resource);
                }catch(MessagingException e){
                    logger.error("添加内联资源 异常 subject={},imageResourceSet={},resourcePath={}",
                            JSONObject.toJSONString(subject), JSONObject.toJSONString(imageResourceSet),imageResource.getImageFilePath());
                    throw new BackstageBusinessException(ApiReturnCode.HTTP_ERROR.getMessage(),ApiReturnCode.HTTP_ERROR.getCode());
                }
            }
        }
    }

    //设置邮件的基本信息
    private boolean handleBasicInfo(SimpleMailMessage simpleMailMessage, MailTempLetDTO mailTempLetDTO) {
        try {
            //设置发件人
            simpleMailMessage.setFrom(userName);
            //设置邮件的主题
            simpleMailMessage.setSubject(mailTempLetDTO.getMainTheme());
            //设置邮件的内容
            simpleMailMessage.setText(mailTempLetDTO.getContent());
            //设置邮件的收件人
            simpleMailMessage.setTo(mailTempLetDTO.getAcceptList());
            if (mailTempLetDTO.getCopyList().length > 0) {
                //设置邮件的抄送人
                simpleMailMessage.setCc(mailTempLetDTO.getCopyList());
            }
            if (mailTempLetDTO.getBccList().length > 0)
                //设置邮件的密送人：MimeMessageHelper # Assert.notNull(bcc, "Bcc address array must not be null");
                simpleMailMessage.setBcc(mailTempLetDTO.getBccList());
            return true;
        } catch (Exception e) {
            logger.error("发送邮件信息异常 simpleMailMessage={},mailTempLetDTO={}",
                    JSONObject.toJSONString(simpleMailMessage), JSONObject.toJSONString(mailTempLetDTO));
        }
        return false;
    }
    private boolean handleBasicInfo(MimeMessageHelper mimeMessageHelper, MailTempLetDTO mailTempLetDTO,boolean isHtml) {
        try {
            //设置必要的邮件元素
            //设置发件人
            mimeMessageHelper.setFrom(userName);
            //设置邮件的主题
            mimeMessageHelper.setSubject(mailTempLetDTO.getMainTheme());
            //设置邮件的内容，区别是否是HTML邮件
            mimeMessageHelper.setText(mailTempLetDTO.getContent(),isHtml);
            //设置邮件的收件人
            mimeMessageHelper.setTo(mailTempLetDTO.getAcceptList());
            //设置非必要的邮件元素，在使用helper进行封装时，这些数据都不能够为空
            if (mailTempLetDTO.getCopyList() != null  && mailTempLetDTO.getCopyList().length > 0)
                //设置邮件的抄送人：MimeMessageHelper # Assert.notNull(cc, "Cc address array must not be null");
                mimeMessageHelper.setCc(mailTempLetDTO.getCopyList());

            if (mailTempLetDTO.getBccList() != null  && mailTempLetDTO.getBccList().length > 0)
                //设置邮件的密送人：MimeMessageHelper # Assert.notNull(bcc, "Bcc address array must not be null");
                mimeMessageHelper.setBcc(mailTempLetDTO.getBccList());
            return true;
        } catch (Exception e) {
            logger.error("发送邮件信息异常 mimeMessageHelper={},mailTempLetDTO={}",
                    JSONObject.toJSONString(mimeMessageHelper), JSONObject.toJSONString(mailTempLetDTO));
        }
        return false;
    }

    /**
     * 用于处理附件信息，附件需要 MimeMessage 对象
     *
     * @param mimeMessageHelper：处理附件的信息对象
     * @param /subject：邮件的主题，用于日志记录
     * @param /attachmentFilePaths：附件文件的路径，该路径要求可以定位到本机的一个资源
     */
    private void handleAttachment(MimeMessageHelper mimeMessageHelper, MailTempLetDTO mailTempLetDTO) {
        //判断是否需要处理邮件的附件
        FileSystemResource resource;
        String fileName;
        //循环处理邮件的附件
        for (String attachmentFilePath : mailTempLetDTO.getAttachmenturl()) {
            //获取该路径所对应的文件资源对象
            resource = new FileSystemResource(new File(attachmentFilePath));
            //判断该资源是否存在，当不存在时仅仅会打印一条警告日志，不会中断处理程序。
            // 也就是说在附件出现异常的情况下，邮件是可以正常发送的，所以请确定你发送的邮件附件在本机存在
            if (!resource.exists()) {
                logger.warn("邮件->{} 的附件->{} 不存在！", mailTempLetDTO.getMainTheme(), attachmentFilePath);
                //开启下一个资源的处理
                continue;
            }
            //获取资源的名称
            fileName = resource.getFilename();
            try {
                //添加附件
                mimeMessageHelper.addAttachment(fileName, resource);
            } catch (MessagingException e) {
                logger.error("发送邮件信息异常 mimeMessageHelper={},mailTempLetDTO={}",
                        JSONObject.toJSONString(mimeMessageHelper), JSONObject.toJSONString(mailTempLetDTO));
            }
        }
    }

}
